﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Windows.Forms;
using System.Threading;
using System.ComponentModel;
using System.Reflection;
using System.IO;
using System.ServiceProcess;
using System.Net;
using System.Data;
using System.Text.RegularExpressions;
using System.Net.Mail;

namespace ETF50CAMBulletin.DownLoadService
{
    public class Program : ServiceBase
    {
        private static readonly string Prefix = "ETF50CAMBulletin";

        //定时清理控制台
        private static System.Timers.Timer timerConsole = new System.Timers.Timer();
        //定时更新企业数据
        private static System.Timers.Timer timerChannel = new System.Timers.Timer();


        private static int RefreshConsoleTime = Convert.ToInt32(ConfigurationManager.AppSettings["RefreshConsoleTime"]) * 1000;
        private static int ReCheckTime = Convert.ToInt32(ConfigurationManager.AppSettings["ReCheckTime"])*1000*60*60;
        private static int SleepTime = Convert.ToInt32(ConfigurationManager.AppSettings["SleepTime"]);
        private static int SSLMailPort = Convert.ToInt32(ConfigurationManager.AppSettings["SSLMailPort"]);
        private static int RunOnce = Convert.ToInt32(ConfigurationManager.AppSettings["RunOnce"]);

        private static int Debug = 0;

        private static string LocalFilePath = ConfigurationManager.AppSettings["LocalFilePath"];
        private static string ServiceRealName = ConfigurationManager.AppSettings["ServiceName"];
        private static string ServiceDisPlayName = ConfigurationManager.AppSettings["ServiceDisPlayName"];
        private static string ServiceDescription = ConfigurationManager.AppSettings["ServiceDescription"];


        private static string Mailto = ConfigurationManager.AppSettings["Mailto"];
        private static string MailFrom = ConfigurationManager.AppSettings["MailFrom"];
        private static string SMTPHOST = ConfigurationManager.AppSettings["SMTPHOST"];
        private static string MailUID = ConfigurationManager.AppSettings["MailUID"];
        private static string MailPWD = ConfigurationManager.AppSettings["MailPWD"];

        private static bool LogToFile = ConfigurationManager.AppSettings["LogToFile"] == "1" ? true : false;
        private static bool SendMail = ConfigurationManager.AppSettings["SendMail"] == "1" ? true : false;
        private static bool EnableSsl = ConfigurationManager.AppSettings["EnableSsl"] == "1" ? true : false;

        private static object obj = new object();

        //服务所在路径
        private static string ServicePath = Application.ExecutablePath;
        private static string ServiceDirectory = Path.GetDirectoryName(ServicePath);

        #region 应用程序入口

        static void Main(string[] args)
        {
            Thread.GetDomain().UnhandledException += new UnhandledExceptionEventHandler(Program_UnhandledException);
            Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);

            bool exists = ServiceControl.CheckService(ServiceRealName);

            if (args.Length == 0)
            {
                ServiceBase.Run(new ServiceBase[] { new Program() });
            }
            else
            {
                string parm = args[0].ToLower();

                switch (parm)
                {

                    case "-debug":
                    case "/debug":

                        Debug = 1;

                        if (exists)
                        {
                            ServiceControllerStatus status = ServiceControl.GetServiceStatus(ServiceRealName);

                            if (status != ServiceControllerStatus.Stopped)
                            {
                                Console.WriteLine(GetDateTime() + ServiceRealName + " 服务正在运行，无法进入调试状态。请先停止该服务");
                                Console.ReadLine();
                            }
                            else
                            {
                                InitilizeCompoment();
                            }
                        }
                        else
                        {
                            InitilizeCompoment();
                        }


                        break;

                    case "-install":
                    case "-setup":
                    case "/install":
                    case "/setup":

                        if (exists)
                        {
                            Console.WriteLine(GetDateTime() + ServiceRealName + " 服务已经存在，如果您想升级服务，请先卸载该服务");
                            Console.ReadLine();
                        }
                        else
                        {
                            Console.WriteLine(GetDateTime() + ServicePath);

                            bool result = ServiceControl.InstallService(ServicePath, ServiceRealName, ServiceDisPlayName, ServiceDescription);

                            if (result)
                            {
                                Console.WriteLine(GetDateTime() + "服务安装成功");
                                Console.WriteLine(GetDateTime() + "正在启动服务...");

                                ServiceControl.StartService(ServiceRealName);

                                while (!(ServiceControl.GetServiceStatus(ServiceRealName) == ServiceControllerStatus.Running))
                                {
                                    while (!(ServiceControl.GetServiceStatus(ServiceRealName) == ServiceControllerStatus.Running))
                                    {
                                        Thread.Sleep(SleepTime);
                                    }
                                }

                                Console.WriteLine(GetDateTime() + "服务已经启动");
                            }
                            else
                            {
                                Console.WriteLine(GetDateTime() + "服务安装失败");
                            }

                            Console.ReadLine();
                        }
                        break;

                    case "-uninstall":
                    case "-remove":
                    case "/uninstall":
                    case "/remove":

                        if (!exists)
                        {
                            Console.WriteLine(GetDateTime() + ServiceRealName + " 服务尚未安装");
                        }
                        else
                        {
                            bool result = ServiceControl.UnInstallService(ServiceRealName);

                            if (!result)
                            {
                                Console.WriteLine(GetDateTime() + "服务卸载失败");
                            }
                            else
                            {
                                Console.WriteLine(GetDateTime() + "服务卸载成功");
                            }
                        }

                        Console.ReadLine();
                        break;
                }

            }
        }

        static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            Output("未处理A：" + e.Exception.Message, ConsoleColor.Red, true);
        }

        static void Program_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            if (e.ExceptionObject is System.Exception)
            {
                Output("未处理P：" + ((System.Exception)e.ExceptionObject).Message, ConsoleColor.Red, true);
            }

        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {

            }

            base.Dispose(disposing);
        }

        public Program()
        {
            base.ServiceName = ServiceRealName;
            base.AutoLog = true;
        }

        #endregion

        #region 初始化应用程序
        /// <summary>
        /// 初始化应用程序
        /// </summary>
        private static void InitilizeCompoment()
        {
            if (Debug == 1)
            {
                Console.Title = ServiceDisPlayName;
            }

            Output("程序初始化", true);

            //初始化定时器
            InitilizeTimer();

            Output("初始化定时器", true);

            Output("工作线程初始化", true);

            Output("文件检查启动", true);

            //SendMailUseSMTP();//测试邮件

            /*************************************************
             * 
             * Add by LanceZhang 11.18
             * Check the local file path, if the folder is not exist,
             * create it. 
             * 
             *************************************************/
            if (Directory.Exists(LocalFilePath) == false)//如果不存在就创建file文件夹
            {
                try
                {
                    Directory.CreateDirectory(LocalFilePath);
                    Output("本地路径" + LocalFilePath + "不存在，已经自动创建", ConsoleColor.Blue, true);
                }
                catch
                {
                    Output("本地路径" + LocalFilePath + "不存在，自动创建失败，请重新设置路径", ConsoleColor.Red, true);
                }
            }


            ManageETF50CAMBulletin();

            if (Debug == 1)
            {
                if (RunOnce == 0)
                {
                    Console.ReadLine();
                }
            }
        }

        #endregion

        #region 业务逻辑

        /// <summary>
        /// 定时检查文件
        /// </summary>
        private static void ManageETF50CAMBulletin()
        {
            if(isTodayHoliday())
            {
                Output("今日休市", true);
                return;
            }

            if (File.Exists(GetTodaysFilePath()))
            {
                Output("今日清单文件已存在", true);
                return;
            }
            else
            {
                //开始获取文件
                GetETF50CAMBulletin();
            }
        }

        /// <summary>
        /// 获取ETF50CAMBulletin
        /// </summary>
        private static void GetETF50CAMBulletin()
        {
            try
            {
                if (GetETF50CAMBulletinFromShangZheng())
                {
                    return;
                }

                if (GetETF50CAMBulletinFromHuaXia())
                {
                    return;
                }

                Output("今日清单获取失败，如果今天非休市日，则为网络故障引起",ConsoleColor.Blue,true);
                SendMailUseSMTP();//发送邮件
            }
            catch (Exception ex)
            {
                Output("从上证所获取数据错误，错误信息为："+ex.Message, ConsoleColor.Red, true);
                SendMailUseSMTP();//发送邮件
            }
        }

        /// <summary>
        /// 从上证所获取ETF50CAMBulletin
        /// </summary>
        private static bool GetETF50CAMBulletinFromShangZheng()
        {
            try
            {

                #region 获取上证所最新清单日期

                StringBuilder source = new StringBuilder();

                WebClient Client = new WebClient();
                Stream strm = Client.OpenRead("http://www.sse.com.cn/sseportal/webapp/datapresent/ETF50Bulletin?reportName=etf50bulletin");
                StreamReader sr = new StreamReader(strm);
                string line;
                do
                {
                    line = sr.ReadLine();
                    source.AppendLine(line);
                }
                while (line != null);
                strm.Close();


                MatchCollection matches = Regex.Matches(source.ToString(), @"ETF50downlad\?SEQ_ID=(\d+)");
                string tt = matches[0].Groups[1].ToString();

                MatchCollection matches1 = Regex.Matches(source.ToString(), @"class=""date"">(\d{4})-(\d{2})-(\d{2})</span>");
                string tt1 = matches1[0].Groups[1].ToString() + matches1[0].Groups[2].ToString() + matches1[0].Groups[3].ToString();

                #endregion

                Output("从上证所获取最新公告日期：" + tt1, ConsoleColor.Blue, true);
                Output("今日日期：" + DateTime.Today.ToString("yyyyMMdd"), ConsoleColor.Blue, true);

                //如果最新公告日期晚于今日，则判定为今日休市
                if (Convert.ToInt32(tt1).CompareTo(Convert.ToInt32(DateTime.Today.ToString("yyyyMMdd"))) < 0)
                {
                    Output("今日休市", ConsoleColor.Blue, true);
                    return true;
                }
                else if (Convert.ToInt32(tt1).CompareTo(Convert.ToInt32(DateTime.Today.ToString("yyyyMMdd"))) > 0)
                    //如果最新公告日期早于今日，则下载最新清单
                {
                    try
                    {
                        Client.DownloadFile("http://www.sse.com.cn/sseportal/webapp/datapresent/ETF50downlad?SEQ_ID=" + tt, GetTodaysFilePath(tt1));
                        Output("从上证所获取数据成功，清单已保存为：" + GetTodaysFilePath(tt1), ConsoleColor.Green, true);
                    }
                    catch (Exception ex1)
                    {
                        Output("从上证所获取数据错误，错误信息为：" + ex1.Message, ConsoleColor.Red, true);
                        GetETF50CAMBulletinFromHuaXia(tt1);
                    }

                    //确保今日的清单已经下载
                    try 
                    {
                        Client.DownloadFile("http://www.sse.com.cn/sseportal/webapp/datapresent/ETF50downlad?SEQ_ID=" + tt, GetTodaysFilePath());
                        Output("从上证所获取数据成功，清单已保存为：" + GetTodaysFilePath(), ConsoleColor.Green, true);
                    }
                    catch (Exception ex2)
                    {
                        Output("从上证所获取数据错误，错误信息为：" + ex2.Message, ConsoleColor.Red, true);
                        GetETF50CAMBulletinFromHuaXia();
                    }

                    return true;
                }
                else //正常情况，直接下载
                {
                    Client.DownloadFile("http://www.sse.com.cn/sseportal/webapp/datapresent/ETF50downlad?SEQ_ID=" + tt, GetTodaysFilePath());
                    Output("从上证所获取数据成功，清单已保存为：" + GetTodaysFilePath(), ConsoleColor.Green, true);
                    return true;
                }
            }
            catch (Exception ex)
            {
                Output("从上证所获取数据错误，错误信息为：" + ex.Message, ConsoleColor.Red, true);
                return false;
            }
        }

        /// <summary>
        /// 从华夏基金获取ETF50CAMBulletin
        /// </summary>
        private static bool GetETF50CAMBulletinFromHuaXia()
        {
            try
            {
                WebClient Client = new WebClient();
                Client.DownloadFile("http://www.chinaamc.com/portal/cn/ETF50/ETF50CAMBulletin"+DateTime.Today.ToString("yyyyMMdd")+".txt", GetTodaysFilePath());

                if (CheckFileSize())
                {
                    Output("从华夏基金获取数据成功，清单已保存为：" + GetTodaysFilePath(), ConsoleColor.Green, true);
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                Output("从华夏基金所获取数据错误，错误信息为：" + ex.Message, ConsoleColor.Red, true);
                return false;
            }
        }

        /// <summary>
        /// 从华夏基金获取ETF50CAMBulletin
        /// </summary>
        /// <param name="yyyyMMdd">下载清单的日期</param>
        private static bool GetETF50CAMBulletinFromHuaXia(string yyyyMMdd)
        {
            try
            {
                WebClient Client = new WebClient();
                Client.DownloadFile("http://www.chinaamc.com/portal/cn/ETF50/ETF50CAMBulletin" + yyyyMMdd + ".txt", GetTodaysFilePath());

                if (CheckFileSize())
                {
                    Output("从华夏基金获取数据成功，清单已保存为：" + GetTodaysFilePath(yyyyMMdd), ConsoleColor.Green, true);
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                Output("从华夏基金所获取数据错误，错误信息为：" + ex.Message, ConsoleColor.Red, true);
                return false;
            }
        }

        /// <summary>
        /// 检查本地文件大小是否合格，如不合格则删除文件
        /// </summary>
        /// <returns>合格则返回true</returns>
        private static bool CheckFileSize()
        {
            if (new FileInfo(GetTodaysFilePath()).Length < 1000)
            {
                try
                {
                    File.Delete(GetTodaysFilePath());
                }
                catch (Exception ex)
                {
                    Output("删除文件"+GetTodaysFilePath()+"失败，错误信息为：" + ex.Message, ConsoleColor.Red, true);
                }
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// 当天是否周末休市
        /// </summary>
        /// <returns></returns>
        private static bool isTodayHoliday()
        {
            if (DateTime.Now.DayOfWeek == DayOfWeek.Saturday || DateTime.Now.DayOfWeek == DayOfWeek.Sunday)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 获取当日文件全路径
        /// </summary>
        /// <returns></returns>
        private static string GetTodaysFilePath()
        {
            return LocalFilePath + Prefix + DateTime.Today.ToString("yyyyMMdd")+".txt";
        }

        /// <summary>
        /// 获取某日文件全路径，重载
        /// </summary>
        /// <param name="yyyyMMdd">某日的日期</param>
        /// <returns></returns>
        private static string GetTodaysFilePath(string yyyyMMdd)
        {
            return LocalFilePath + Prefix + yyyyMMdd + ".txt";
        }

        /// <summary>
        /// 发送邮件
        /// </summary>
        private static void SendMailUseSMTP()
        {
            try
            {
                System.Net.Mail.MailMessage msg = new System.Net.Mail.MailMessage();
                msg.To.Add(Mailto);

                msg.From = new MailAddress(MailFrom, "ETF50CAMBulletin Download Service", System.Text.Encoding.UTF8);

                msg.Subject = "An Error Occurs when we download today's bulletin list";//邮件标题 
                msg.SubjectEncoding = System.Text.Encoding.UTF8;//邮件标题编码 
                msg.Body = "";//邮件内容 
                msg.BodyEncoding = System.Text.Encoding.UTF8;//邮件内容编码 
                msg.IsBodyHtml = true;//是否是HTML邮件 
                msg.Priority = MailPriority.High;//邮件优先级 

                StreamReader din = new StreamReader(ServiceDirectory + @"\log_" + DateTime.Now.ToString("yyyyMMdd") + ".txt", System.Text.Encoding.Default);
                while (din.Peek() > -1)
                {
                    msg.Body = msg.Body + din.ReadToEnd();
                }
                din.Close();   

                SmtpClient client = new SmtpClient();
                client.Credentials = new System.Net.NetworkCredential(MailUID, MailPWD);

                if (EnableSsl)
                {
                    client.Port = SSLMailPort;//587;//Gmail使用的端口 
                    client.EnableSsl = true;//经过ssl加密 
                }

                client.Host = SMTPHOST;

                object userState = msg;

                client.SendAsync(msg, userState);
                //client.Send(msg);

                Output("邮件已经发送", true);
            }
            catch (System.Net.Mail.SmtpException ex)
            {
                Output("发送邮件出错，错误信息为" + ex.Message, true);
            }
        }

        #endregion

        #region 初始化服务设置
        protected override void OnStart(string[] args)
        {
            InitilizeCompoment();
        }

        protected override void OnStop()
        {
            //停止定时器
            timerConsole.Enabled = false;
            timerChannel.Enabled = false;
        }

        #endregion

        #region 初始化定时器
        /// <summary>
        /// 初始化定时器
        /// </summary>
        private static void InitilizeTimer()
        {
            // 定时清理控制台
            timerConsole.Interval = RefreshConsoleTime;
            timerConsole.Enabled = true;
            timerConsole.Elapsed += new System.Timers.ElapsedEventHandler(timerConsole_Elapsed);
            timerConsole.AutoReset = true;

            //定时进行文件检查
            timerChannel.Interval = ReCheckTime;
            timerChannel.Enabled = true;
            timerChannel.Elapsed += new System.Timers.ElapsedEventHandler(timerCheckFile_Elapsed);
            timerChannel.AutoReset = true;
        }

        static void timerCheckFile_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Output("文件检查启动", true);
            ManageETF50CAMBulletin();
        }

        /// <summary>
        /// debug模式下定时清空控制台
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        static void timerConsole_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (Debug == 1)
            {
                Console.Clear();
                Output("控制台已更新");
            }
        }

        #endregion

        #region 调试辅助逻辑
        private static string GetDateTime()
        {
            return "[" + DateTime.Now.ToString("HH:mm:ss") + "] ";
        }

        private static void Output(string msg)
        {
            lock (obj)
            {
                if (Debug == 1)
                {
                    Console.WriteLine(GetDateTime() + msg);
                }
            }

        }

        private static void Output(string msg, bool log)
        {
            lock (obj)
            {
                if (Debug == 1)
                {
                    Console.WriteLine(GetDateTime() + msg);
                }

                if (log)
                {
                    if (LogToFile)
                    {
                        Log.WriteToFile(ServiceDirectory + @"\log_" + DateTime.Now.ToString("yyyyMMdd") + ".txt", GetDateTime() + msg);
                    }
                }
            }

        }

        private static void Output(string msg, ConsoleColor color)
        {
            lock (obj)
            {
                if (Debug == 1)
                {
                    Console.ForegroundColor = color;
                    Console.WriteLine(GetDateTime() + msg);
                    Console.ResetColor();
                }
            }
        }

        private static void Output(string msg, ConsoleColor color, bool log)
        {
            lock (obj)
            {
                if (Debug == 1)
                {
                    Console.ForegroundColor = color;
                    Console.WriteLine(GetDateTime() + msg);
                    Console.ResetColor();
                }

                if (log)
                {
                    if (LogToFile)
                    {
                        Log.WriteToFile(ServiceDirectory + @"\log_" + DateTime.Now.ToString("yyyyMMdd") + ".txt", GetDateTime() + msg);
                    }
                }
            }
        }
        #endregion
    }
}
